/************************************************************************

								Heroes IV
					  Copyright 2000, The 3DO Company

	------------------------------------------------------------------
  						 image_sequence.cpp

	$Header: /heroes4/image_sequence.cpp $

	$NoKeywords: $

 ************************************************************************/

#include "precompiled.h"
#pragma warning( disable: 4786 ) // Disable the ever so helpful "identifier was truncated to '255' characters" warning

#include "image_sequence.h"

#include <cctype>
#include <vector>

#include "format_string.h"
#include "pixel_24.h"
#include "streambuf_operators.h"

bool g_disable_base_frames_drawing = false;
bool g_disable_base_shadow_drawing = false;
bool g_disable_shadow_drawing = false;
bool g_disable_frames_drwaing = false;

// ------------------------------------------------------------------------------
// Unnamed namespace
// ------------------------------------------------------------------------------

namespace
{

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	int const k_current_format_version = 3;

	// ------------------------------------------------------------------------------
	// t_layer_type enum
	// ------------------------------------------------------------------------------

	enum t_layer_type
	{
		k_layer_none = -1,
		k_layer_frame,
		k_layer_shadow,
	};

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	struct t_layer_info
	{
		t_layer_type		type;
		std::vector< int >	frame_num_vector;

		t_layer_info() : type( k_layer_none ) {}
	};

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	inline int round_to_nearest( double arg )
	{
		if ( arg > 0.0 )
			arg += 0.5;
		else
			arg -= 0.5;
		return arg;
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	bool parse_layer_name( char const * layer_name, t_layer_info & layer_info )
	{
		t_layer_type new_layer_type;
		std::vector< int > new_frame_num_vector;
		
		if ( memicmp( layer_name, "frame ", sizeof( "frame " ) - 1 ) == 0 )
		{
			new_layer_type = k_layer_frame;
			layer_name += sizeof( "frame " ) - 1;
		}
		else if ( memicmp( layer_name, "shadow ", sizeof( "shadow " ) - 1 ) == 0 )
		{
			new_layer_type = k_layer_shadow;
			layer_name += sizeof( "shadow " ) - 1;
		}
		else
			return false;
		
		std::string frame_string;
		while ( *layer_name != '\0' )
		{
			while ( isspace( *layer_name ) )
				++layer_name;
			if ( *layer_name == '\0' )
				break;
			
			frame_string.resize( 0 );
			while ( !isspace( *layer_name ) && *layer_name != '\0' )
			{
				frame_string += *layer_name;
				++layer_name;
			}

			int frame_number = atoi( frame_string.c_str() );
			if ( frame_number >= 1 )
				new_frame_num_vector.push_back( frame_number - 1 );
		}

		if ( new_frame_num_vector.empty() )
			return false;

		layer_info.type = new_layer_type;
		layer_info.frame_num_vector.swap( new_frame_num_vector );

		return true;
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	std::string build_layer_name( t_layer_info const & layer_info )
	{
		assert( layer_info.type == k_layer_frame || layer_info.type == k_layer_shadow );
		assert( !layer_info.frame_num_vector.empty() );

		std::string result = layer_info.type == k_layer_frame ? "frame" : "shadow";

		std::vector< int >::const_iterator frame_num_end = layer_info.frame_num_vector.end();
		std::vector< int >::const_iterator frame_num_iter = layer_info.frame_num_vector.begin();
		for ( ; frame_num_iter != frame_num_end; ++frame_num_iter )
			result += format_string( " %03i", *frame_num_iter + 1 );

		return result;
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	void copy_frame_info_vector(
		std::vector< t_image_sequence_frame_info > const &	source,
		std::vector< t_image_sequence_frame_info > & 		dest )
	{
		typedef std::vector< t_image_sequence_frame_info >	t_vector;

		t_vector result( source.size() );

		t_vector::const_iterator source_iter = source.begin();
		t_vector::iterator dest_iter = result.begin();
		for ( ; source_iter != source.end(); ++source_iter, ++dest_iter )
		{
			dest_iter->index = source_iter->index;
			dest_iter->shadow_index = source_iter->shadow_index;
		}

		result.swap( dest );
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	template < typename t_bitmap_group_arg >
	void compute_extents(
		t_bitmap_group_arg const &							frames,
		std::vector< t_image_sequence_frame_info > const &	frame_info_vector,
		t_screen_rect &										rect,
		t_screen_rect &										shadow_rect )
	{
		typedef std::vector< t_image_sequence_frame_info > t_frame_info_vector;

		static t_uint32 const k_invalid_index = std::numeric_limits< t_uint32 >::max();

		t_screen_rect new_rect( 0, 0, 0, 0 );
		t_frame_info_vector::const_iterator frame_info_iter = frame_info_vector.begin();
		for (	;
				frame_info_iter != frame_info_vector.end();
				++frame_info_iter )
		{
			typename t_bitmap_group_arg::size_type index = frame_info_iter->index;
			if ( index != k_invalid_index )
			{
				//with delta compression a frame could have no rect size.
				if (frames[ index ].get_rect().width()  == 0 && 
					frames[ index ].get_rect().height() == 0)
					continue;

				// Found the first frame
				new_rect = frames[ index ].get_rect();
		
				// Combine the rect with all the other frames
				for (	++frame_info_iter;
						frame_info_iter != frame_info_vector.end();
						++frame_info_iter )
				{
					index = frame_info_iter->index;
					if ( index != k_invalid_index &&
						frames[ index ].get_rect().width()  != 0 && 
						frames[ index ].get_rect().height() != 0)
						new_rect = get_extent( new_rect, frames[ index ].get_rect() );
				}
				break;
			}
		}

		t_screen_rect new_shadow_rect( 0, 0, 0, 0 );
		for (	frame_info_iter = frame_info_vector.begin();
				frame_info_iter != frame_info_vector.end();
				++frame_info_iter )
		{
			typename t_bitmap_group_arg::size_type index = frame_info_iter->shadow_index;
			if ( index != k_invalid_index )
			{	
				//with delta compression a frame could have no rect size.
				if (frames[ index ].get_rect().width()  == 0 && 
					frames[ index ].get_rect().height() == 0)
					continue;

				// Found the first frame
				new_shadow_rect = frames[ index ].get_rect();

				// Combine the rect with all the other frames
				for (	++frame_info_iter;
						frame_info_iter != frame_info_vector.end();
						++frame_info_iter )
				{
					index = frame_info_iter->shadow_index;
					if ( index != k_invalid_index &&
						frames[ index ].get_rect().width()  != 0 && 
						frames[ index ].get_rect().height() != 0)
						new_shadow_rect = get_extent( new_shadow_rect, frames[ index ].get_rect() );
				}
				break;
			}
		}

		rect = new_rect;
		shadow_rect = new_shadow_rect;
	}

	// ------------------------------------------------------------------------------
	// ------------------------------------------------------------------------------
	class t_bitmap_group_offsetter
	{
	public:
		// Constructor/Destructor
		t_bitmap_group_offsetter( t_bitmap_group_24 & group, t_screen_point const & offset );
		~t_bitmap_group_offsetter();

	private:
		// Data members
		t_bitmap_group_24 &		m_group;
		t_screen_point			m_offset;
	};

	t_bitmap_group_offsetter::t_bitmap_group_offsetter( t_bitmap_group_24 & group, t_screen_point const & offset )
		:	m_group( group ),
			m_offset( offset )
	{
		m_group.offset( m_offset );
	}

	t_bitmap_group_offsetter::~t_bitmap_group_offsetter()
	{
		m_group.offset( -m_offset );
	}

} // Unnamed namespace

// ------------------------------------------------------------------------------
// t_image_sequence_base_base class members
// ------------------------------------------------------------------------------

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_image_sequence_base_base::~t_image_sequence_base_base()
{
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_image_sequence_base_base::delete_frame( int frame_num )
{
	assert( frame_num >= 0 && frame_num < get_frame_count() );

	int frame_count = m_frame_info_vector.size();
	for ( ; frame_num + 1 < frame_count; ++frame_num )
		m_frame_info_vector[ frame_num ].index = m_frame_info_vector[ frame_num + 1 ].index;
	if ( m_frame_info_vector[ frame_num ].shadow_index == empty_frame_index() )
		m_frame_info_vector.pop_back();
	else
		m_frame_info_vector[ frame_num ].index = empty_frame_index();

	resync_frames();
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_image_sequence_base_base::delete_shadow( int frame_num )
{
	assert( frame_num >= 0 && frame_num < get_frame_count() );

	int frame_count = m_frame_info_vector.size();
	for ( ; frame_num + 1 < frame_count; ++frame_num )
		m_frame_info_vector[ frame_num ].shadow_index = m_frame_info_vector[ frame_num + 1 ].shadow_index;
	if ( m_frame_info_vector[ frame_num ].index == empty_frame_index() )
		m_frame_info_vector.pop_back();
	else
		m_frame_info_vector[ frame_num ].shadow_index = empty_frame_index();

	resync_frames();
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_image_sequence_base_base::duplicate_frame( int frame_num )
{
	assert( frame_num >= 0 && frame_num < get_frame_count() );

	if ( m_frame_info_vector.back().index != empty_frame_index() )
		m_frame_info_vector.push_back( t_frame_info() );

	int other_frame_num;
	for (	other_frame_num = m_frame_info_vector.size() - 1;
			other_frame_num > frame_num;
			--other_frame_num )
	{
		m_frame_info_vector[ other_frame_num ].index =
			m_frame_info_vector[ other_frame_num - 1 ].index;
	}

	resync_frames();
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_image_sequence_base_base::duplicate_shadow( int frame_num )
{
	assert( frame_num >= 0 && frame_num < get_frame_count() );

	if ( m_frame_info_vector.back().shadow_index != empty_frame_index() )
		m_frame_info_vector.push_back( t_frame_info() );

	int other_frame_num;
	for (	other_frame_num = m_frame_info_vector.size() - 1;
			other_frame_num > frame_num;
			--other_frame_num )
	{
		m_frame_info_vector[ other_frame_num ].shadow_index =
			m_frame_info_vector[ other_frame_num - 1 ].shadow_index;
	}

	resync_frames();
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_image_sequence_base_base::move_frame(
	int	source_frame_num,
	int	dest_frame_num )
{
	assert( source_frame_num >= 0 && source_frame_num < get_frame_count() );
	assert( dest_frame_num >= 0 && dest_frame_num < get_frame_count() );

	if ( source_frame_num == dest_frame_num )
		return;

	t_uint32 index = m_frame_info_vector[ source_frame_num ].index;
	int frame_num = source_frame_num;
	if ( dest_frame_num < source_frame_num )
	{
		for ( ; frame_num > dest_frame_num; --frame_num )
			m_frame_info_vector[ frame_num ].index = m_frame_info_vector[ frame_num - 1 ].index;
	}
	else
	{
		for ( ; frame_num < dest_frame_num; ++frame_num )
			m_frame_info_vector[ frame_num ].index = m_frame_info_vector[ frame_num + 1 ].index;
	}
	m_frame_info_vector[ dest_frame_num ].index = index;

	resync_frames();
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_image_sequence_base_base::move_shadow(
	int	source_frame_num,
	int	dest_frame_num )
{
	assert( source_frame_num >= 0 && source_frame_num < get_frame_count() );
	assert( dest_frame_num >= 0 && dest_frame_num < get_frame_count() );

	if ( source_frame_num == dest_frame_num )
		return;

	t_uint32 index = m_frame_info_vector[ source_frame_num ].shadow_index;
	int frame_num = source_frame_num;
	if ( dest_frame_num < source_frame_num )
	{
		for ( ; frame_num > dest_frame_num; --frame_num )
			m_frame_info_vector[ frame_num ].shadow_index = m_frame_info_vector[ frame_num - 1 ].shadow_index;
	}
	else
	{
		for ( ; frame_num < dest_frame_num; ++frame_num )
			m_frame_info_vector[ frame_num ].shadow_index = m_frame_info_vector[ frame_num + 1 ].shadow_index;
	}
	m_frame_info_vector[ dest_frame_num ].shadow_index = index;

	resync_frames();
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
void t_image_sequence_base_base::resync_frames()
{
	typedef std::vector< t_layer_info >	t_layer_info_vector;

	// Construct the layer info for each layer by running through the frames
	t_uint32 layer_count = get_layer_count();
	t_layer_info_vector layer_info_vector( layer_count );

	int frame_count = get_frame_count();
	int frame_num;
	for ( frame_num = 0; frame_num < frame_count; ++frame_num )
	{
		t_frame_info const & frame_info = m_frame_info_vector[ frame_num ];

		if ( frame_info.index != empty_frame_index() )
		{
			assert( frame_info.index < layer_count );
			t_layer_info & layer_info = layer_info_vector[ frame_info.index ];
			assert(		layer_info.type == k_layer_none
					||	layer_info.type == k_layer_frame );
			layer_info.type = k_layer_frame;
			layer_info.frame_num_vector.push_back( frame_num );
		}

		if ( frame_info.shadow_index != empty_frame_index() )
		{
			assert( frame_info.shadow_index < layer_count );
			t_layer_info & layer_info = layer_info_vector[ frame_info.shadow_index ];
			assert(		layer_info.type == k_layer_none
					||	layer_info.type == k_layer_shadow );
			layer_info.type = k_layer_shadow;
			layer_info.frame_num_vector.push_back( frame_num );
		}
	}

	// Kill any layers which aren't used by any frames
	t_uint32 layer_num = layer_count;
	while ( layer_num > 0 )
	{
		--layer_num;
		if ( layer_info_vector[ layer_num ].type == k_layer_none )
		{
			delete_layer( layer_num );
			layer_info_vector.erase( layer_info_vector.begin() + layer_num );

			// Fixup the later layers
			t_uint32 other_layer_num;
			for ( other_layer_num = layer_num; other_layer_num < layer_count; ++other_layer_num )
			{
				t_layer_info const & layer_info = layer_info_vector[ other_layer_num ];
				t_uint32 t_frame_info::* index_mem_ptr =
					layer_info.type == k_layer_frame ? &t_frame_info::index : &t_frame_info::shadow_index;

				// Adjust this layer's index in the frame info vector
				std::vector< int >::const_iterator frame_num_end = layer_info.frame_num_vector.end();
				std::vector< int >::const_iterator frame_num_iter = layer_info.frame_num_vector.begin();
				for ( ; frame_num_iter != frame_num_end; ++frame_num_iter )
				{
					t_uint32 & index = m_frame_info_vector[ *frame_num_iter ].*index_mem_ptr;
					assert( index == other_layer_num + 1 );
					--index;
				}
			}
		}
	}

	// Now rebuild the layer names
	for ( layer_num = 0; layer_num < layer_count; ++layer_num )
	{
		t_layer_base & layer_base = get_layer_base( layer_num );
		t_layer_info const & layer_info = layer_info_vector[ layer_num ];
		layer_base.set_name( build_layer_name( layer_info ) );
	}
}

// ------------------------------------------------------------------------------
// caluate_frame_rect_lists : create a list of rectangles for each frame which bounds the non-transparent pixels.
// used to minimize overdraw.
// ------------------------------------------------------------------------------
void t_image_sequence_base_base::caluate_frame_rect_lists()
{





}

// ------------------------------------------------------------------------------
// t_image_sequence_24 class members
// ------------------------------------------------------------------------------

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
bool t_image_sequence_24::set_frames( t_bitmap_group_24 const & bitmap_group )
{
	int num_frames   = 0;
	t_screen_rect	extent_shadow_base;
	t_screen_rect	extent_frames_base;

	//reset the image type varaibles with a new bitmap_group

	m_image_sequence_type	= k_image_sequence_standard;
	m_base_frame			= k_image_no_base_frame;
	m_base_shadow			= k_image_no_base_frame;
	m_show_base_frames		= true;
	
	// First find out how many frames there are
	t_bitmap_group_24::const_iterator layer_iter = bitmap_group.begin();
	for ( ; layer_iter != bitmap_group.end(); ++layer_iter )
	{
		t_layer_info layer_info;
		if ( parse_layer_name( layer_iter->get_name().c_str(), layer_info ) )
		{
			std::vector< int >::const_iterator frame_num_end = layer_info.frame_num_vector.end();
			std::vector< int >::const_iterator frame_num_iter = layer_info.frame_num_vector.begin();
			for ( ; frame_num_iter != frame_num_end; ++frame_num_iter )
			{
				if ( *frame_num_iter >= num_frames )
					num_frames = *frame_num_iter + 1;
			}
		}
	}

	// Build the new frames and frame info
	t_bitmap_group_24 new_frames;
	int sequence_ndx = 0;
	std::vector< t_frame_info > new_frame_info_vector( num_frames );
	for ( layer_iter = bitmap_group.begin(); layer_iter != bitmap_group.end(); ++layer_iter )
	{
		t_layer_info layer_info;
		if ( parse_layer_name( layer_iter->get_name().c_str(), layer_info ) )
		{
			std::vector< int >::const_iterator frame_num_end = layer_info.frame_num_vector.end();
			std::vector< int >::const_iterator frame_num_iter = layer_info.frame_num_vector.begin();
			for ( ; frame_num_iter != frame_num_end; ++frame_num_iter )
			{
				t_frame_info & frame_info = new_frame_info_vector[ *frame_num_iter ];
				t_uint32 * index_ptr;
				
				if ( layer_info.type == k_layer_frame )
					index_ptr = &frame_info.index;
				else
					index_ptr = &frame_info.shadow_index;
				
				if ( *index_ptr != empty_frame_index() )
					return false;
				
				*index_ptr = new_frames.size();
			}
			new_frames.push_back( *layer_iter );
			sequence_ndx++;
		}
		else
		{
			if ( memicmp( layer_iter->get_name().c_str(), "base_shadow", sizeof( "base_shadow" ) - 1 ) == 0 )
			{
				m_image_sequence_type = k_image_sequence_delta_compressed;
				m_base_shadow = sequence_ndx;
				new_frames.push_back( *layer_iter );
				sequence_ndx++;

				extent_shadow_base = layer_iter->get_rect();
			}
			
			if ( memicmp( layer_iter->get_name().c_str(), "base_frame", sizeof( "base_frame" ) - 1 ) == 0 )
			{
				m_image_sequence_type = k_image_sequence_delta_compressed;
				m_base_frame = sequence_ndx;
				new_frames.push_back( *layer_iter );
				sequence_ndx++;

				extent_frames_base = layer_iter->get_rect();
			}
		}
	}

	m_offset = t_screen_point( 0, 0 );
	compute_extents( new_frames, new_frame_info_vector, m_rect, m_shadow_rect );
	m_frames.swap( new_frames );
	m_frame_info_vector.swap( new_frame_info_vector );

	if (m_base_shadow!=k_image_no_base_frame)
		m_shadow_rect = get_extent(m_shadow_rect,extent_shadow_base);
	
	if (m_base_frame!=k_image_no_base_frame)
		m_rect = get_extent(m_rect,extent_frames_base);

	if (m_frame_info_vector.size() == 0)
		return false;

	caluate_frame_rect_lists();

	return true;
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
bool t_image_sequence_24::read( std::streambuf & stream )
{
	int version;
	try
	{
		version = get< t_uint16 >( stream );
		
		if (version >= 3)
		{
			m_image_sequence_type	= get< t_uint16 >( stream );
			m_base_frame			= get< t_int16 >( stream );
			m_base_shadow			= get< t_int16 >( stream );
		}
	}
	catch ( t_streambuf_read_failure const & )
	{
		return false;
	}

	return read_version( stream, version );
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
bool t_image_sequence_24::read_version( std::streambuf & stream, int version, std::string * owner_name_ptr )
{
	t_bitmap_group_24 frames;
	t_screen_point offset;
	std::string owner_name;

	try
	{
		if ( !frames.read( stream, 0 ) )
			return false;
		stream >> offset;
		if ( version == 1 )
			stream >> owner_name;
	}
	catch ( t_streambuf_read_failure const & )
	{
		return false;
	}

	if ( !set_frames( frames ) )
		return false;
	set_offset( offset );
	if ( owner_name_ptr != 0 )
		owner_name.swap( *owner_name_ptr );

	return true;
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
bool t_image_sequence_24::write( std::streambuf & stream ) const
{
	try
	{
		put< t_uint16 >( stream, k_current_format_version );
		
		put< t_uint16 >( stream, m_image_sequence_type );
		put< t_int16 >( stream, m_base_frame );
		put< t_int16 >( stream, m_base_shadow );

		if ( !get_frames().write( stream ) )
			return false;
		stream << get_offset();
	}
	catch ( t_streambuf_write_failure const & )
	{
		return false;
	}

	return true;
}

// ------------------------------------------------------------------------------
// t_image_sequence members
// ------------------------------------------------------------------------------

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_image_sequence::t_image_sequence(
	t_image_sequence_24 const &	other,
	t_screen_point const &		offset )
{
	t_bitmap_group new_frames( other.get_frames() );

	std::vector< t_frame_info > new_frame_info_vector;
	copy_frame_info_vector( other.get_frame_info_vector(), new_frame_info_vector );

	m_offset = other.get_offset() + offset;
	m_rect = other.get_rect() + offset;
	m_shadow_rect = other.get_shadow_rect() + offset;
	m_frames.swap( new_frames );
	m_frame_info_vector.swap( new_frame_info_vector );
	
	m_image_sequence_type = other.get_sequence_type();
	m_base_frame  = other.get_base_frame();
	m_base_shadow = other.get_base_shadow();
}

// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
t_image_sequence::t_image_sequence(
								   t_image_sequence_24 const &	other,
								   double						ratio,
								   t_screen_point const &		offset )
{
	t_bitmap_group new_frames;
	{
		t_bitmap_group_24 & source_frames = const_cast< t_image_sequence_24 & >( other ).get_frames();
		t_bitmap_group_offsetter offsetter( source_frames, other.get_offset() + offset );
		
		scale_group( source_frames, new_frames, ratio );
	}
	
	std::vector< t_frame_info > new_frame_info_vector;
	copy_frame_info_vector( other.get_frame_info_vector(), new_frame_info_vector );
	
	m_offset = t_screen_point( 0, 0 );
	compute_extents( new_frames, new_frame_info_vector, m_rect, m_shadow_rect );
	m_frames.swap( new_frames );
	m_frame_info_vector.swap( new_frame_info_vector );
	
	m_image_sequence_type = other.get_sequence_type();
	m_base_frame  = other.get_base_frame();
	m_base_shadow = other.get_base_shadow();
}
